// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/events/platform/x11/x11_event_source_glib.h"

#include <X11/Xlib.h>
#include <glib.h>

namespace ui {

namespace {

    struct GLibX11Source : public GSource {
        // Note: The GLibX11Source is created and destroyed by GLib. So its
        // constructor/destructor may or may not get called.
        XDisplay* display;
        GPollFD* poll_fd;
    };

    gboolean XSourcePrepare(GSource* source, gint* timeout_ms)
    {
        GLibX11Source* gxsource = static_cast<GLibX11Source*>(source);
        if (XPending(gxsource->display))
            *timeout_ms = 0;
        else
            *timeout_ms = -1;
        return FALSE;
    }

    gboolean XSourceCheck(GSource* source)
    {
        GLibX11Source* gxsource = static_cast<GLibX11Source*>(source);
        return XPending(gxsource->display);
    }

    gboolean XSourceDispatch(GSource* source,
        GSourceFunc unused_func,
        gpointer data)
    {
        X11EventSource* x11_source = static_cast<X11EventSource*>(data);
        x11_source->DispatchXEvents();
        return TRUE;
    }

    GSourceFuncs XSourceFuncs = {
        XSourcePrepare,
        XSourceCheck,
        XSourceDispatch,
        NULL
    };

} // namespace

X11EventSourceGlib::X11EventSourceGlib(XDisplay* display)
    : event_source_(this, display)
{
    InitXSource(ConnectionNumber(display));
}

X11EventSourceGlib::~X11EventSourceGlib()
{
    g_source_destroy(x_source_);
    g_source_unref(x_source_);
}

void X11EventSourceGlib::ProcessXEvent(XEvent* xevent)
{
    DispatchEvent(xevent);
}

void X11EventSourceGlib::StopCurrentEventStream()
{
    event_source_.StopCurrentEventStream();
}

void X11EventSourceGlib::OnDispatcherListChanged()
{
    event_source_.OnDispatcherListChanged();
}

void X11EventSourceGlib::InitXSource(int fd)
{
    DCHECK(!x_source_);
    DCHECK(event_source_.display()) << "Unable to get connection to X server";

    x_poll_.reset(new GPollFD());
    x_poll_->fd = fd;
    x_poll_->events = G_IO_IN;
    x_poll_->revents = 0;

    GLibX11Source* glib_x_source = static_cast<GLibX11Source*>(
        g_source_new(&XSourceFuncs, sizeof(GLibX11Source)));
    glib_x_source->display = event_source_.display();
    glib_x_source->poll_fd = x_poll_.get();

    x_source_ = glib_x_source;
    g_source_add_poll(x_source_, x_poll_.get());
    g_source_set_can_recurse(x_source_, TRUE);
    g_source_set_callback(x_source_, NULL, &event_source_, NULL);
    g_source_attach(x_source_, g_main_context_default());
}

// static
scoped_ptr<PlatformEventSource> PlatformEventSource::CreateDefault()
{
    return make_scoped_ptr(new X11EventSourceGlib(gfx::GetXDisplay()));
}

} // namespace ui
